package com.boredom.Guts;

import java.lang.UnsupportedOperationException;
import java.util.Collection;
import java.util.LinkedList;
import java.util.Iterator;
import java.io.File;
import java.io.FileOutputStream;
import java.io.FileInputStream;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.util.Date;
import java.util.Map;
import java.util.HashMap;
import java.io.Serializable;
import java.io.FileNotFoundException;
import java.io.IOException;

/**
 * This is the intermediary between the GUI and Guts.
 * The front end should interact only with the Manager,
 * and the Groups, Events, and settings it provides access to.
 */

public class Manager {
    private static boolean recentlyUpdated;

    public static synchronized Collection<Group> getGroups() {
	return FileManager.getGroups();
    }

    public static synchronized Collection<Group> getMembers(Group group) {
	Collection<Group> members = new LinkedList<Group>();
	if (group.hasChildren()) {
	    Iterator<String> iter = group.getChildren().iterator();
	    while (iter.hasNext())
		members.addAll(getMembers(FileManager.readGroup(iter.next())));
	}
	else
	    members.add(group);
	return members;
    }

    public static synchronized void createGroup(Group group) {
	FileManager.writeGroup(group);
    }

    public static synchronized void addMember(Group group, Group member) {
	group.add(member.getName());
	FileManager.writeGroup(group);
    }

    public static synchronized void removeMember(Group group, Group member) {
	group.remove(member.getName());
	FileManager.writeGroup(group);
    }

    public static synchronized Collection<Event> getEvents(String groupName) {
	Collection<Event> events = new LinkedList<Event>();
	File[] eventFiles = FileManager.getEventFiles(groupName);
	for (int i = 0; i < eventFiles.length; i++)
	    events.add(FileManager.readEvent(groupName, eventFiles[i].getName()));
	return events;
    }

    public static synchronized Collection<Event> getEvents(Group group) {
	return getEvents(group.getName());
    }

    public static synchronized void createEvent(Event event) {
	FileManager.writeEvent(event);
    }

    public static synchronized void modifyEvent(Event modifiedEvent) {
	FileManager.writeEvent(modifiedEvent);
    }

    public static synchronized Settings getSettings() {
	throw new UnsupportedOperationException();
    }

    public static synchronized void updateSettings(Settings settings) {
	throw new UnsupportedOperationException();
    }

    public static synchronized boolean hasUpdated() {
	if (recentlyUpdated) {
	    recentlyUpdated = false;
	    return true;
	}
	else
	    return false;
    }

    /* For the network manager: */

    public static synchronized void update(File file, byte[] contents) {
	try {
	    recentlyUpdated = true;
	    FileOutputStream stream = new FileOutputStream(file);
	    stream.write(contents);
	}
	catch (FileNotFoundException e) {
	    throw new RuntimeException("Internal IO Error");
	}
	catch (IOException e) {
	    throw new RuntimeException("Internal IO Error");
	}
    }

    public static synchronized byte[] read(File file) {
	try {
	    int length = (int) file.length();
	    byte[] bytes = new byte[length];
	    int numBytes = 0;
	    FileInputStream stream = new FileInputStream(file);
	    while (numBytes < length)
		numBytes += stream.read(bytes, numBytes, length - numBytes);
	    return bytes;
	}
	catch (FileNotFoundException e) {
	    throw new RuntimeException("Internal IO Error");
	}
	catch (IOException e) {
	    throw new RuntimeException("Internal IO Error");
	}
    }

    public static synchronized Map<File, Date> getModificationTimes(String groupName) {
	Map<File, Date> times = new HashMap<File, Date>();
	File groupFile = FileManager.getGroupFile(groupName);
	File[] eventFiles = FileManager.getEventFiles(groupName);
	times.put(groupFile, new Date(groupFile.lastModified()));
	for (int i = 0; i < eventFiles.length; i++)
	    times.put(eventFiles[i], new Date(eventFiles[i].lastModified()));
	return times;
    }
}

class Settings {}



/**
 * Handle reading and writing Groups and Events to files.
 *
 * File structure:
 * groups/<groupName>/group
 * groups/<groupName>/events/<eventName>
 */
class FileManager {
    private static final File groupDirectory = new File("groups");
    private static final String eventFolder = "events";

    /* Path helpers */
    public static File getGroupFile(String groupName) {
	return new File(groupDirectory + "/" + groupName + "/group");
    }

    public static File getEventFile(String groupName, String eventName) {
	return new File(groupDirectory + "/" + groupName + "/" +
			eventFolder + "/" + eventName);
    }

    public static File[] getEventFiles(String groupName) {
	File eventsDirectory =  new File(groupDirectory + "/" + groupName + "/" +
					 eventFolder);
	return eventsDirectory.listFiles();
    }

    /* Generic IO */
    private static void writeObject(Serializable object, File file) {
	try {
	    ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream(file));
	    stream.writeObject(object);
	}
	catch (FileNotFoundException e) {
	    throw new RuntimeException("Internal IO Error");
	}
	catch (IOException e) {
	    throw new RuntimeException("Internal IO Error");
	}
    }

    private static void readObject(Object object, File file) {
	try {
	    ObjectInputStream stream = new ObjectInputStream(new FileInputStream(file));
	    object = stream.readObject();
	}
	catch (FileNotFoundException e) {
	    throw new RuntimeException("Internal IO Error");
	}
	catch (IOException e) {
	    throw new RuntimeException("Internal IO Error");
	}
	catch (ClassNotFoundException e) {
	    throw new RuntimeException("Internal IO Error");
	}
    }

    /* Group IO */
    public static Group readGroup(String name) {
	Group group = new Group();
	readObject(group, getGroupFile(name));
	return group;
    }

    public static void writeGroup(Group group) {
	writeObject(group, getGroupFile(group.getName()));
    }

    public static Collection<Group> getGroups() {
	Collection<Group> groups = new LinkedList<Group>();
	File[] files = groupDirectory.listFiles();
	for (int i = 0; i < files.length; i++) {
	    groups.add(readGroup(files[i].getName()));
	}
	return groups;
    }

    /* Event IO */
    public static Event readEvent(String groupName, String eventName) {
	Event event = new Event();
	readObject(event, getEventFile(groupName, eventName));
	return event;
    }

    public static void writeEvent(Event event) {
	writeObject(event, getEventFile(event.getGroup(), event.getName()));
    }	
}

